package bje;

import robocode.Robot;
import robocode.ScannedRobotEvent;
import java.awt.Color;
import java.awt.geom.Point2D;

/**
 * Implements the 'Surfer' robot, which continually follows a "squared reversed C" shape path,
 * clockwise and counterclockwise around the right half of the battlefield. Stops and scans for
 * other robots at top center, top right corner, bottom right corner, and bottom center positions.
 * Shoots 1-3 bullets at a time when a robot is scanned based on proximity. Adjusts fire power based
 * on distance to the enemy. Conserves Robot energy by not continuously firing.
 * 
 * @author Jeffrey Beck
 */
public class Surfer extends Robot {

  /** The amount to rotate the radar in degrees. **/
  private static double RADAR_TURN = 360;
  /** The amount to rotate the Robot in degrees. **/
  private static double ROBOT_TURN = 90;

  /** The battlefield width in pixels. **/
  double fieldWidth;
  /** The battlefield height in pixels. **/
  double fieldHeight;
  /** Robot's x position in pixels. **/
  double robotPositionX;
  /** Robot's y position in pixels. **/
  double robotPositionY;
  /** The battlefield center X position in pixels. **/
  double centerX;
  /** The battlefield center X position in pixels. **/
  double offsetFromCenterX = 0.0;

  /**
   * Moves this robot within the right half of the battlefield, stops, scans, and shoots. Called by
   * the Robocode game engine.
   */
  public void run() {

    // Sets this Robots' colors.
    setColors(Color.RED, Color.WHITE, Color.BLACK);

    // Turn radar independent of turning the Robot.
    setAdjustRadarForRobotTurn(true);

    while (true) {

      // Moves this robot to it's initial position, stops, scans and fires if enemy robot detected.
      moveToStartPosition();

      // Moves this robot clockwise, stops, scans and fires if enemy robot detected.
      moveClockwise();

      // Moves this robot counterclockwise, stops, scans and fires if enemy robot detected.
      moveCounterClockwise();

    }

  }

  /**
   * Moves this Robot to top center, stops, scans, and fires if enemy robot detected.
   */
  public void moveToStartPosition() {

    // Sets battlefield and position information.
    fieldWidth = getBattleFieldWidth();
    fieldHeight = getBattleFieldHeight();
    robotPositionY = getY();
    robotPositionX = getX();
    centerX = fieldWidth / 2;
    offsetFromCenterX = centerX - robotPositionX;

    // Initially positions this Robot by moving to top center and facing left.
    turnLeft(getHeading());
    ahead(fieldHeight - robotPositionY);
    turnLeft(ROBOT_TURN);

    if (offsetFromCenterX < 0) {
      ahead(Math.abs(offsetFromCenterX));
    }
    else {
      back(offsetFromCenterX);
    }
  }

  /**
   * Moves this Robot from top center to bottom center. Stops, scans, and fires if robot detected in
   * 4 places: top center, top right corner, bottom right corner, and bottom center.
   * 
   * @return the x,y position at the end of the move.
   */
  public Point2D.Double moveClockwise() {

    // moves forward to top right corner and scans for robots to shoot
    turnRadarRight(RADAR_TURN);
    back(centerX);
    turnRadarRight(RADAR_TURN);

    // moves to bottom right corner and scans for robots to shoot
    turnLeft(ROBOT_TURN);
    ahead(fieldHeight);
    turnRadarRight(RADAR_TURN);

    // moves forward to bottom center and scans for robots to shoot
    turnRadarRight(RADAR_TURN);
    turnRight(ROBOT_TURN);
    ahead(centerX);
    turnRadarRight(RADAR_TURN);

    return new Point2D.Double(getX(), getY());
  }

  /**
   * Moves this Robot counterclockwise from bottom center to top center. Stops, scans, and fires if
   * robot detected in 4 places: top center, top right corner, bottom right corner, and bottom
   * center.
   * 
   * @return the x,y position at the end of the move.
   */
  public Point2D.Double moveCounterClockwise() {

    // moves backwards to bottom right corner and scans for robots to shoot
    back(centerX);
    turnRadarRight(RADAR_TURN);

    // moves to top right corner and scans for robots to shoot
    turnRight(ROBOT_TURN);
    ahead(fieldHeight);
    turnRadarRight(RADAR_TURN);

    // moves forward to top center and scans for robots to shoot
    turnLeft(ROBOT_TURN);
    ahead(centerX);
    turnRadarRight(RADAR_TURN);

    return new Point2D.Double(getX(), getY());
  }

  /**
   * Turns gun and fires at scanned robots with a power that is based on distance from target, and
   * bullets at a time.
   * 
   * @param e contains information about the enemy robot, e.g. its location
   */
  public void onScannedRobot(ScannedRobotEvent e) {
    double distance = e.getDistance();

    double firingAngle = getHeading() - getGunHeading() + e.getBearing();

    double firePower = calcFirePower(fieldWidth, distance);
    int numberOfBullets = calcNumBullets(distance);

    stop();
    turnGunRight(firingAngle);

    for (int i = 0; i <= numberOfBullets; i++) {
      fire(firePower);
    }
    scan();
    resume();

  }

  /**
   * Calculates the power with which to fire bullets, based on on distance from the target.
   * 
   * @param distance the distance to the enemy.
   * @param fieldWidth the width of the battlefield in pixels.
   * @return the calculated fire power, between 0.1 and 3
   */
  public double calcFirePower(double fieldWidth, double  distance) {

    double result = 0;
    double tempFieldWidth = fieldWidth;
    double tempDistance = distance;

    // enforce a reasonable minimum enemy distance for the calculation
    if (fieldWidth <= 0) {
      tempFieldWidth = 400;
    }

    // make sure we do not divide by zero
    if (distance <= 0) {
      tempDistance = 400;
    }



    result = tempFieldWidth / tempDistance;

    result = ((result) < 0.1) ? 0.1 : result;

    return Math.min(result, 3);

  }

  /**
   * Calculates how many bullets to fire (1, 2, or 3) based on the distance from the target.
   * 
   * @param distance the the distance to the enemy
   * @return the number of bullets to be fired
   */
  public int calcNumBullets(double distance) {

    int numberOfBullets = 1;

    if ((distance <= 200) && (distance >= 0)) {
      numberOfBullets = 3;
    }
    else if ((distance <= 300) && (distance >= 201)) {
      numberOfBullets = 2;
    }

    return numberOfBullets;
  }

}
